package kr.poturns.blink.internal;

import java.io.IOException;

import kr.poturns.blink.internal.comm.BluetoothDeviceX;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

/**
 * 
 * @author YeonHo.Kim
 * @since 2014.07.17
 *
 */
public class BluetoothAssistant implements Runnable {

	// *** CONSTANT DECLARATION *** //
	public final static String ACTION_STATE_ON = "android.bluetooth.adapter.action.STATE_ON";

	
	// *** STATIC DECLARATION *** //
	private static BluetoothAssistant sInstance = null;
	
	/**
	 * BluetoothAssistant의 Singleton-인스턴스를 반환한다.
	 * @param manager ( :{@link InterDeviceManager} )
	 * @return manager가 Null일 경우, 기존의 Instance를 반환한다.
	 */
	public static BluetoothAssistant getInstance(InterDeviceManager manager) {
		if (sInstance == null && manager != null)
			sInstance = new BluetoothAssistant(manager);
		return sInstance;
	}

	/**
	 * 블루투스 통신에서 감지해야하는 IntentFilter를 얻어온다.
	 * @return 
	 */
	public static IntentFilter obtainIntentFilter() {
		IntentFilter mIntentFilter = new IntentFilter();
		mIntentFilter.addAction(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		mIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
		mIntentFilter.addAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		mIntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
		mIntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
		mIntentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
		
		mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
		mIntentFilter.addAction(BluetoothDevice.ACTION_FOUND);
		
		mIntentFilter.addAction(BluetoothAssistant.ACTION_STATE_ON);
		
		return mIntentFilter;
	}

	
	// *** FIELD DECLARATION *** //
	private final InterDeviceManager INTER_DEV_MANAGER;
	private final BluetoothManager BLUETOOTH_MANAGER;
	
	private BluetoothGattServer BLUETOOTH_GATT_SERVER;
	private boolean isAbleBLE;
	
	public BluetoothAssistant(InterDeviceManager manager) {
		this.INTER_DEV_MANAGER = manager;
		this.BLUETOOTH_MANAGER = (BluetoothManager) manager.getContext().getSystemService(Context.BLUETOOTH_SERVICE);
	}
	
	public void init() {
		Context mContext = INTER_DEV_MANAGER.getContext();
		BluetoothAdapter mAdapter = BLUETOOTH_MANAGER.getAdapter();
		
		int state = mAdapter.getState();
		switch (state) {
		case BluetoothAdapter.STATE_OFF:
			mContext.startActivity(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
			break;
			
		case BluetoothAdapter.STATE_ON:
			startClassicServer();
			break;
		}

		BLUETOOTH_GATT_SERVER = BLUETOOTH_MANAGER.openGattServer(INTER_DEV_MANAGER.getContext(), mBluetoothGattServerCallback);
		BLUETOOTH_GATT_SERVER.close();
		
		isAbleBLE = DeviceAnalyzer.getInstance(mContext).hasBluetoothLE;
	}

	private final Object SERVER_THREAD_LOCK = new Object();
	private boolean isServerActivated = false;
	private Thread mServerThread = null;
	
	private BluetoothGattServer mBluetoothGattServer;
	
	public void startClassicServer() {
		if (mServerThread != null) 
			if (mServerThread.isAlive())
				return;
		
		synchronized (SERVER_THREAD_LOCK) {
			isServerActivated = true;
		}
		
		mServerThread = new Thread(this, "ServerThread");
		mServerThread.start();
	}
	
	public void stopClassicServer() {
		synchronized (SERVER_THREAD_LOCK) {
			isServerActivated = false;

			if (mServerThread != null) {
				try {
					mServerThread.join(5000);
					
				} catch (InterruptedException e) {
					
				} finally {
					mServerThread = null;
				}
			}
		}
	}

	@Override
	public void run() {
		BluetoothAdapter mAdapter = BLUETOOTH_MANAGER.getAdapter();
		
		BluetoothServerSocket mSocket;
		try {
			mSocket = mAdapter.listenUsingRfcommWithServiceRecord(null, null);
		
			
			
		} catch (IOException e) {
			mSocket = null;
			e.printStackTrace();
		}
		
		while (isServerActivated) {
			
			try {
				BluetoothSocket socket = mSocket.accept();
				
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	private boolean isLeScanning = false;
	
	public void startDiscovery() {
		BluetoothAdapter mAdapter = BLUETOOTH_MANAGER.getAdapter();

		if (isLeScanning || !(isLeScanning = true))
			mAdapter.stopLeScan(INTER_DEV_MANAGER);
		mAdapter.startLeScan(INTER_DEV_MANAGER);	
		
		if (mAdapter.isDiscovering())
			mAdapter.cancelDiscovery();
		mAdapter.startDiscovery();
		
	}
	
	public void stopDiscovery() {
		BluetoothAdapter mAdapter = BLUETOOTH_MANAGER.getAdapter();

		if (isLeScanning && (isLeScanning = false))
			mAdapter.stopLeScan(INTER_DEV_MANAGER);	
		
		if (mAdapter.isDiscovering())
			mAdapter.cancelDiscovery();
	}

	public void connectDevice(BluetoothDeviceX deviceX) {
		LinkStatusHandler mNetworkManager = LinkStatusHandler.getInstance(INTER_DEV_MANAGER);
		BluetoothDevice device = deviceX.getDevice();
		
		if ((deviceX.getSource() & BluetoothDeviceX.SOURCE_LE) != 0) {
			BluetoothGatt mBluetoothGatt = device.connectGatt(INTER_DEV_MANAGER.getContext(), deviceX.getAutoConnect(), mBluetoothGattCallback);
			 
			//if (mBluetoothGatt.connect()) {
			//	mNetworkManager.registerConnection(deviceX, mBluetoothGatt);
			//}
			
		} else {
			BluetoothSocket mBluetoothSocket;
			try {
				if (deviceX.getSecure()) 
					mBluetoothSocket = device.createRfcommSocketToServiceRecord(null);
				else
					mBluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(null);
			
				mBluetoothSocket.connect();
				mNetworkManager.registerConnection(deviceX, mBluetoothSocket);
				
			} catch (IOException e) {
				
			}
		}
	}
	
	public void disconnectDevice(BluetoothDeviceX deviceX) {
		LinkStatusHandler mNetworkManager = LinkStatusHandler.getInstance(INTER_DEV_MANAGER);
		BluetoothDevice device = deviceX.getDevice();
		
		LinkStatusHandler.getInstance(INTER_DEV_MANAGER).unregisterConnection(deviceX);
	}
	
	public Thread startSocketPolling(BluetoothSocket socket) {
		// TODO Auto-generated catch block
		Thread thread = new Thread();
		
		try {
			socket.getInputStream().read();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return thread;
	}
	
	public void startWritingPacket(BluetoothSocket socket, String message) {
		// TODO Auto-generated catch block
		
		try {
			socket.getOutputStream().write(message.getBytes());
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
		
		@Override
		public void onCharacteristicChanged(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic) {
			// TODO Auto-generated method stub
			super.onCharacteristicChanged(gatt, characteristic);
		}
		
		@Override
		public void onCharacteristicRead(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			// TODO Auto-generated method stub
			super.onCharacteristicRead(gatt, characteristic, status);
		}
		
		@Override
		public void onCharacteristicWrite(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			// TODO Auto-generated method stub
			super.onCharacteristicWrite(gatt, characteristic, status);
		}
		
		@Override
		public void onConnectionStateChange(BluetoothGatt gatt, int status,
				int newState) {
			// TODO Auto-generated method stub
			super.onConnectionStateChange(gatt, status, newState);
		}
		
		@Override
		public void onDescriptorRead(BluetoothGatt gatt,
				BluetoothGattDescriptor descriptor, int status) {
			// TODO Auto-generated method stub
			super.onDescriptorRead(gatt, descriptor, status);
		}
		
		@Override
		public void onDescriptorWrite(BluetoothGatt gatt,
				BluetoothGattDescriptor descriptor, int status) {
			// TODO Auto-generated method stub
			super.onDescriptorWrite(gatt, descriptor, status);
		}
		
		@Override
		public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
			// TODO Auto-generated method stub
			super.onReadRemoteRssi(gatt, rssi, status);
		}
		
		@Override
		public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
			// TODO Auto-generated method stub
			super.onReliableWriteCompleted(gatt, status);
		}
		
		@Override
		public void onServicesDiscovered(BluetoothGatt gatt, int status) {
			// TODO Auto-generated method stub
			super.onServicesDiscovered(gatt, status);
		}
	}; 
	
	private BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {
		
		@Override
		public void onCharacteristicReadRequest(BluetoothDevice device,
				int requestId, int offset,
				BluetoothGattCharacteristic characteristic) {
			// TODO Auto-generated method stub
			super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
		}
		
		@Override
		public void onCharacteristicWriteRequest(BluetoothDevice device,
				int requestId, BluetoothGattCharacteristic characteristic,
				boolean preparedWrite, boolean responseNeeded, int offset,
				byte[] value) {
			// TODO Auto-generated method stub
			super.onCharacteristicWriteRequest(device, requestId, characteristic,
					preparedWrite, responseNeeded, offset, value);
		}
		
		@Override
		public void onConnectionStateChange(BluetoothDevice device, int status,
				int newState) {
			// TODO Auto-generated method stub
			super.onConnectionStateChange(device, status, newState);
		}
		
		@Override
		public void onDescriptorReadRequest(BluetoothDevice device,
				int requestId, int offset, BluetoothGattDescriptor descriptor) {
			// TODO Auto-generated method stub
			super.onDescriptorReadRequest(device, requestId, offset, descriptor);
		}
		
		@Override
		public void onDescriptorWriteRequest(BluetoothDevice device,
				int requestId, BluetoothGattDescriptor descriptor,
				boolean preparedWrite, boolean responseNeeded, int offset,
				byte[] value) {
			// TODO Auto-generated method stub
			super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite,
					responseNeeded, offset, value);
		}
		
		@Override
		public void onExecuteWrite(BluetoothDevice device, int requestId,
				boolean execute) {
			// TODO Auto-generated method stub
			super.onExecuteWrite(device, requestId, execute);
		}
		
		@Override
		public void onServiceAdded(int status, BluetoothGattService service) {
			// TODO Auto-generated method stub
			super.onServiceAdded(status, service);
		}
	};
}
